import jofc2.model.Chart
import jofc2.model.elements.LineChart
import jofc2.model.axis.YAxis
import jofc2.model.axis.XAxis
import jofc2.model.axis.Label
import jofc2.model.elements.AreaHollowChart
import jofc2.model.elements.PieChart
import jofc2.model.axis.Label.Rotation
import jofc2.model.elements.BarChart
import jofc2.OFC
import jofc2.model.elements.FilledBarChart
import jofc2.model.elements.SketchBarChart
import jofc2.model.elements.HorizontalBarChart
import jofc2.model.elements.ScatterChart
import java.math.MathContext
import jofc2.model.elements.StackedBarChart
import jofc2.model.elements.StackedBarChart.StackValue

class PspDataController {

    // Burn dowm value of each team member
    ArrayList<ArrayList<Double>> teamBalanceList;
    ArrayList<Double> avgBalanceList;
	
    def authenticateService
	
    User currentUser = new User()
    private static projectId
    private static chartType = "team"
    private static colorCode = ["#FF33FF","#FF4500","#9400D3","#87CEFF"]
    private static team = []
    def index = {
        redirect(action: "list", params: params)
    }
	
    def list = {
        def authPrincipal = authenticateService.principal()
        currentUser = User.findByUsername(authPrincipal.username)
        List projectList = new ArrayList()
        for(projectTeam in currentUser.projectTeams){
            projectList.add (projectTeam.project)
        }
        session.projectList = projectList
		
        if (params.project) {
            def project = Project.findById(params.project.id)
			
            // keep current project into the session
            session.project = project
            session.projectTeams = ProjectTeam.findAllByProject(project)
			
            [project: project]
        }
    }
	
    def show = {
        String strCond = ""
        String strSQLTeamList = ""
        Integer intID = 0
        def projectTeams
		
        // Parameters from list page[userId and taskId]
        if (params.taskId) {

            projectTeams = ProjectTeam.executeQuery("Select distinct t.user.id, t.user.userRealName From ProjectTeam t, Task ts Where t.project.id = ts.project.id and ts.id=:task and t.user.id = :user Order By t.project.name, t.user.userRealName", [task: params.taskId.toLong(), user: params.userId.toLong()])
			
            for (int i = 0; i < projectTeams.size; i++) {
                strSQLTeamList += ", (Select MIN(p.startDate) as stDate From PspData p, SubTask s2 " +
				"Where p.task.id = s2.task.id and p.phaseName = s2.phaseName and " +
				"p.user.id = " + projectTeams[i][0] + " and s2.id = s.id Group By s2.id) "
				
                strSQLTeamList += ", (Select SUM(p.actualTimeInMin) as actTime From PspData p, SubTask s2 " +
				"Where p.task.id = s2.task.id and p.phaseName = s2.phaseName and " +
				"p.user.id = " + projectTeams[i][0] + " and s2.id = s.id Group By s2.id) "
            }

            def pspList = PspData.executeQuery(
				"Select p.task.project.name, p.task.name, p.phaseName, s.unitForPhase, s.id " + strSQLTeamList +
				"From PspData p, SubTask s Where p.task.id = s.task.id and p.phaseName = s.phaseName and p.task.id=:task and p.user.id=:user " +
				"Group By p.task.project.name, p.task.name, p.phaseName, s.unitForPhase " +
				"Order By p.task.project.name, s.id ", [task: params.taskId.toLong(), user: params.userId.toLong()])

            [pspList: pspList, projectTeams: projectTeams]
			
        } else {
			
            // Select project
            String strCondProTeam = ""
            if (params.project) {
                if (params.project.id != "null") {
                    strCond = "and p.task.project.id=" + params.project.id
					
                    strCondProTeam = "Where project.id = " + params.project.id
                }
            }
			
            if (params.task) {
                if (params.task.id != "null") {
                    strCond += "and p.task.id=" + params.task.id
                }
            }
			
            //			if (params.subtask) {
            //				if (params.subtask.id != "null") {
            //					if (strCond != "") strCond += " and "
            //					strCond += "subTask.id=" + params.subtask.id
            //				}
            //			}
			
            // Select user
            if (params.user) {
                if (params.user.id != "null") {
                    strCond += "and p.user.id=" + params.user.id
                    strCondProTeam += " and user.id = " + params.user.id
                }
            }
			
            projectTeams = ProjectTeam.executeQuery("Select distinct t.user.id, t.user.userRealName From ProjectTeam t " + strCondProTeam + " Order By t.project.name, t.user.userRealName")
			
            for (int i = 0; i < projectTeams.size; i++) {
                strSQLTeamList += ", (Select MIN(p.startDate) as stDate From PspData p, SubTask s2 " +
				"Where p.task.id = s2.task.id and p.phaseName = s2.phaseName and " +
				"p.user.id = " + projectTeams[i][0] + " and s2.id = s.id Group By s2.id) "
				
                strSQLTeamList += ", (Select SUM(p.actualTimeInMin) as actTime From PspData p, SubTask s2 " +
				"Where p.task.id = s2.task.id and p.phaseName = s2.phaseName and " +
				"p.user.id = " + projectTeams[i][0] + " and s2.id = s.id Group By s2.id) "
            }

            def pspList = PspData.executeQuery(
				"Select p.task.project.name, p.task.name, p.phaseName, s.unitForPhase, s.id " + strSQLTeamList +
				"From PspData p, SubTask s Where p.task.id = s.task.id and p.phaseName = s.phaseName " + strCond +
				"Group By p.task.project.name, p.task.name, p.phaseName, s.unitForPhase " +
				"Order By p.task.project.name, s.id ")	
			
            [pspList: pspList, projectTeams: projectTeams]
			
        }
    }

    def getTeamList = {
        def project = Project.findById(params.projectId)
        def projectTeam = ProjectTeam.findAllByProject(project)

        [projectTeam: projectTeam]
    }

    def getTaskList = {
        def project = Project.findById(params.projectId)
        def taskList = Task.findAllByProject(project)
		
        [taskList: taskList]
    }
	
    def burn_down_table = {
        if (params.projectId) {
            def project = Project.findById(params.projectId)

            // range of project date.
            def projectRange = project.endDate - project.startDate

            // put data into array lists preparing for OpenFlashChart
            ArrayList<Integer> noList = new ArrayList<Integer>();
            ArrayList<Integer> dateList = new ArrayList<Integer>();
            ArrayList<String> taskList = new ArrayList<String>();
            ArrayList<Double> planList = new ArrayList<Double>();
            ArrayList<Double> plannedList = new ArrayList<Double>();

            // burn down list contain list of team member Earn Value
            ArrayList<ArrayList<Double>> burnDownList = new ArrayList<ArrayList<Double>>();
            ArrayList<Double> avgBurnDownList = new ArrayList<Double>();

            // Burn dowm value of each team member
            teamBalanceList = new ArrayList<ArrayList<Double>>();
            avgBalanceList = new ArrayList<Double>();

            def team = ProjectTeam.findAllByProject(project)
            for (def t in team) {
                burnDownList.add(new ArrayList<Double>());
                teamBalanceList.add(new ArrayList<Double>());
            }

            // current date beging process
            def date

            // task start date
            def startDate

            // task end date
            def endDate
            
            // task end planning date
            def endPlanDate

            // Planning date range for the Task
            def planDateRange

            def task, subTask

            //  Total units for the task
            def totalTaskUnits

            subTask = SubTask.createCriteria()
            def planned = subTask.get {
                            'in' ("task", project.tasks)
                projections {
                    sum "unitForPhase"
                }
            }
            
            Double[] teamBalance = new Double[team.size()]
            Double avgBalance
            // Total Balance EV remainning for team
            for (int j = 0; j < team.size(); j++) {
                //                teamBalance[j] = 200.0d
                teamBalance[j] = (double)planned
            }
            avgBalance = (double)planned

            def pspData

            for (int i = 0; i < projectRange; i++) {
                noList.add(i + 1);

                date = project.startDate + i
                
                dateList.add(date);

                // if there ia a task for given date, put it into the list, otherwise put null
                task = Task.find("from Task t where (? between t.startDate and t.endDate) and t.project = ?", [date, project])
                if (task) {
                    taskList.add(task)

                    startDate = task.startDate
                    endPlanDate = task.endPlanDate

                    planDateRange = endPlanDate - startDate + 1

                    // if the date is int task plan period
                    if (startDate <= date && date <= endPlanDate) {
                        // calculate total units of task from unitForPhase of SubTask
                        subTask = SubTask.createCriteria()
                        totalTaskUnits = subTask.get {
                            eq ("task", task)
                            projections {
                                sum "unitForPhase"
                            }
                        }
                        if (totalTaskUnits != null) {
                            planList.add(totalTaskUnits / planDateRange)
                            planned -= totalTaskUnits / planDateRange
                        }
                    }
                } else {
                    taskList.add(null)
                }

                // Current date doesn't belongs to any task plan date
                if (planList.size() < i + 1) {
                    planList.add(null)
                }
                plannedList.add(Math.abs((double)planned))

                // Sum of Earn value of every team member.
                def sumEv = 0
                // effort done for each team members
                for (int j = 0; j < team.size(); j++) {
                    def ArrayList<Double> teamBurnDownList = burnDownList.get(j);
                    def ArrayList<Double> balanceList = teamBalanceList.get(j);
                    def ev = 0

                    // Number of phases that this user have pspdata for the current date
                    def phases = PspData.executeQuery("SELECT DISTINCT(phaseName) from PspData where user = ? and task = ?", team[j].user, task)

                    for (p in phases) {
                        if (task) {
                            def dayNumPhase = PspData.executeQuery("SELECT count(*) from PspData where phaseName = ? and user = ? and task = ? and date(startDate) = ?", p, team[j].user, task, date)
                            def totalNumPhase = PspData.executeQuery("SELECT count(*) from PspData where phaseName = ? and user = ? and task = ?", p, team[j].user, task)
                            subTask = SubTask.findByTaskAndPhaseName(task, p.trim())

                            if (totalNumPhase[0] != 0 && subTask != null) {
                                ev += (dayNumPhase[0] / totalNumPhase[0]) * subTask.unitForPhase
                                sumEv += (dayNumPhase[0] / totalNumPhase[0]) * subTask.unitForPhase
                            }
                        }
                    }

                    // find late sending task
                    pspData = null
                    if (task) {
                        pspData = PspData.executeQuery("SELECT distinct(task) from PspData p where user_id = ? and task_id != ? and date(p.startDate) = ? and task_id > 8", team[j].user.id, task.id, date)
                        //                        print('1 ')
                        //                        if (pspData) {
                        //                            task = pspData.task
                        //                        }
                        //                    } else {
                        //                        pspData = PspData.executeQuery("SELECT distinct(task) from PspData p where user_id = ? and date(p.startDate) = ?", team[j].user.id, date)
                        //                        print('2 ')
                    }
                    //                    println(date.toString() + ' ' + pspData.inspect())
                    if (pspData != null && pspData != []) {
                        def inTask = pspData
//                        println(inTask[0])
                        // Number of phases that this user have pspdata for the current date
                        phases = PspData.executeQuery("SELECT DISTINCT(phaseName) from PspData where user = ? and task = ?", team[j].user, inTask[0])

                        for (p in phases) {
                            if (inTask) {
                                def dayNumPhase = PspData.executeQuery("SELECT count(*) from PspData where phaseName = ? and user = ? and task = ? and date(startDate) = ?", p, team[j].user, inTask[0], date)
                                def totalNumPhase = PspData.executeQuery("SELECT count(*) from PspData where phaseName = ? and user = ? and task = ?", p, team[j].user, inTask[0])
                                subTask = SubTask.findByTaskAndPhaseName(inTask[0], p.trim())

                                if (totalNumPhase[0] != 0 && subTask != null) {
                                    ev += (dayNumPhase[0] / totalNumPhase[0]) * subTask.unitForPhase
                                    sumEv += (dayNumPhase[0] / totalNumPhase[0]) * subTask.unitForPhase
                                }
                            }
                        }
                    }

                    teamBurnDownList.add(ev);
                    teamBalance[j] = teamBalance[j] - ev
                    balanceList.add(teamBalance[j])
                }
                avgBurnDownList.add(sumEv / team.size())
                avgBalance -= sumEv / team.size()
                avgBalanceList.add(avgBalance)
            }

            [project: project, projectRange: projectRange, noList: noList, dateList: dateList,
                taskList: taskList, planList: planList, plannedList: plannedList, burnDownList: burnDownList, avgBurnDownList: avgBurnDownList,
                teamBalanceList: teamBalanceList, avgBalanceList: avgBalanceList]
        }
    }

    def burn_down_chart = {
        //		println "projectId111111111111 = "+projectId
        if (projectId) {
            def project = Project.findById(projectId)

            def projectRange = project.endDate - project.startDate

            ArrayList<Integer> noList = new ArrayList<Integer>();
            ArrayList<Integer> dateList = new ArrayList<Integer>();
            ArrayList<String> taskList = new ArrayList<String>();
            ArrayList<Double> planList = new ArrayList<Double>();
            ArrayList<Double> plannedList = new ArrayList<Double>();

            ArrayList<ArrayList<Double>> burnDownList = new ArrayList<ArrayList<Double>>();
            ArrayList<Double> avgBurnDownList = new ArrayList<Double>();

            ArrayList<ArrayList<Double>> teamBalanceList = new ArrayList<ArrayList<Double>>();
            ArrayList<Double> avgBalanceList = new ArrayList<Double>();

            team = ProjectTeam.findAllByProject(project)
            for (def t in team) {
                burnDownList.add(new ArrayList<Double>());
                teamBalanceList.add(new ArrayList<Double>());
            }

            def date

            def startDate

            def endDate
			
            def endPlanDate

            def planDateRange

            def task, subTask

            def totalTaskUnits

            subTask = SubTask.createCriteria()
            def planned = subTask.get {
							'in' ("task", project.tasks)
                projections {
                    sum "unitForPhase"
                }
            }
			
            Double[] teamBalance = new Double[team.size()]
            Double avgBalance
            for (int j = 0; j < team.size(); j++) {
                teamBalance[j] = (double)planned
            }
            avgBalance = (double)planned

            def pspData

            for (int i = 0; i < projectRange; i++) {
                noList.add(i + 1);

                date = project.startDate + i
				
                dateList.add(date);

                task = Task.find("from Task t where (? between t.startDate and t.endDate) and t.project = ?", [date, project])
                if (task) {
                    taskList.add(task)

                    startDate = task.startDate
                    endPlanDate = task.endPlanDate

                    planDateRange = endPlanDate - startDate + 1

                    if (startDate <= date && date <= endPlanDate) {
                        // calculate total units of task from unitForPhase of SubTask
                        subTask = SubTask.createCriteria()
                        totalTaskUnits = subTask.get {
                            eq ("task", task)
                            projections {
                                sum "unitForPhase"
                            }
                        }
                        if (totalTaskUnits != null) {
                            planList.add(totalTaskUnits / planDateRange)
                            planned -= totalTaskUnits / planDateRange
                        }
                    }
                } else {
                    taskList.add(null)
                }

                if (planList.size() < i + 1) {
                    planList.add(null)
                }
                plannedList.add(planned)

                def sumEv = 0
                for (int j = 0; j < team.size(); j++) {
                    def ArrayList<Double> teamBurnDownList = burnDownList.get(j);
                    def ArrayList<Double> balanceList = teamBalanceList.get(j);
                    def ev = 0

                    def phases = PspData.executeQuery("SELECT DISTINCT(phaseName) from PspData where user = ?", team[j].user)

                    for (p in phases) {
                        if (task) {
                            def dayNumPhase = PspData.executeQuery("SELECT count(*) from PspData where phaseName = ? and user = ? and task = ? and date(startDate) = ?", p, team[j].user, task, date)
                            def totalNumPhase = PspData.executeQuery("SELECT count(*) from PspData where phaseName = ? and user = ? and task = ?", p, team[j].user, task)
                            subTask = SubTask.findByTaskAndPhaseName(task, p.trim())

                            if (totalNumPhase[0] != 0 && subTask != null) {
                                ev += (dayNumPhase[0] / totalNumPhase[0]) * subTask.unitForPhase
                                sumEv += (dayNumPhase[0] / totalNumPhase[0]) * subTask.unitForPhase
                            }
                        }
                    }

                    // find late sending task
                    pspData = null
                    if (task) {
                        pspData = PspData.executeQuery("SELECT distinct(task) from PspData p where user_id = ? and task_id != ? and date(p.startDate) = ? and task_id > 8", team[j].user.id, task.id, date)
                    }
                    if (pspData != null && pspData != []) {
                        def inTask = pspData
//                        println(inTask[0])
                        // Number of phases that this user have pspdata for the current date
                        phases = PspData.executeQuery("SELECT DISTINCT(phaseName) from PspData where user = ? and task = ?", team[j].user, inTask[0])

                        for (p in phases) {
                            if (inTask) {
                                def dayNumPhase = PspData.executeQuery("SELECT count(*) from PspData where phaseName = ? and user = ? and task = ? and date(startDate) = ?", p, team[j].user, inTask[0], date)
                                def totalNumPhase = PspData.executeQuery("SELECT count(*) from PspData where phaseName = ? and user = ? and task = ?", p, team[j].user, inTask[0])
                                subTask = SubTask.findByTaskAndPhaseName(inTask[0], p.trim())

                                if (totalNumPhase[0] != 0 && subTask != null) {
                                    ev += (dayNumPhase[0] / totalNumPhase[0]) * subTask.unitForPhase
                                    sumEv += (dayNumPhase[0] / totalNumPhase[0]) * subTask.unitForPhase
                                }
                            }
                        }
                    }

                    teamBurnDownList.add(ev);
                    teamBalance[j] = teamBalance[j] - ev
                    balanceList.add(teamBalance[j])
                }
                avgBurnDownList.add(sumEv / team.size())
                avgBalance -= sumEv / team.size()
                avgBalanceList.add(avgBalance)
            }

            //			[project: project, projectRange: projectRange, noList: noList, dateList: dateList,
            //				taskList: taskList, planList: planList, plannedList: plannedList, burnDownList: burnDownList, avgBurnDownList: avgBurnDownList,
            //				teamBalanceList: teamBalanceList, avgBalanceList: avgBalanceList]
            //			def data1 = new ArrayList<Number>();
            //			def data2 = new ArrayList<Number>();
            //			def data3 = new ArrayList<Number>();
            //			def rand = new Random();
            //			for (int i = 0; i < 10; i++) {
            //			  data1.add(100 + rand.nextInt(5));
            //			  data2.add(7 + rand.nextInt(5));
            //			  data3.add(14 + rand.nextInt(5));
            //			}
            //			println "chartType = "+chartType
            if(chartType == "team"){
                def plannedListChart = new ArrayList<Number>()
                def avgBalanceListChart = new ArrayList<Number>()
                def maxData = -1
                for (i in plannedList){
                    if((int)i > maxData)
                    maxData = (int)i
                    plannedListChart.add((int)i)
                }
                for(i in avgBalanceList){
                    if((int)i > maxData)
                    maxData = (int)i
                    avgBalanceListChart.add((int)i)
                }
                render new Chart("Team Burn Down Chart").setYAxis(new YAxis().setRange(0, maxData+20, 50)).setXAxis(new XAxis().setRange(0, avgBalanceListChart.size(), 7)).addElements(
                    new LineChart().setWidth(4).setColour("#DFC329").setDotSize(5).addValues(plannedListChart),
                    new LineChart().setWidth(4).setColour("#0000FF").setDotSize(5).addValues(avgBalanceListChart))
            }else if(chartType == "personal"){
                def plannedListChart = new ArrayList<Number>()
                def maxData = -1
                //				def colorCode = PspData.colorCode
                def countz = 0
                for (i in plannedList){
                    if((int)i > maxData)
                    maxData = (int)i
                    plannedListChart.add((int)i)
                }
                def c = new Chart("Personal Burn Down Chart").setYAxis(new YAxis().setRange(0, maxData+20, 50)).setXAxis(new XAxis().setRange(0, plannedListChart.size(), 7));
                def linelist = []
                def plannedListLine = new LineChart();
                plannedListLine.setWidth(4).setColour("#DFC329").setDotSize(5);
                plannedListLine.addValues(plannedListChart)
                linelist.add(plannedListLine)
                //				println "teamBalanceList = "+teamBalanceList
				
                for(i in teamBalanceList){
                    def tmpList =  new ArrayList<Number>()
                    for(j in i){
                        tmpList.add((int)j)
                    }
                    //					println " j = "+ tmpList
                    linelist.add(new LineChart().setWidth(2).setColour(colorCode.toArray()[countz++]).setDotSize(5).addValues(tmpList))
                }
				
                c.addElements(linelist);
				
                render c;
            }
        }
    }
    def burn_down_chart_page = {
        projectId = params.projectId
        if(params.chartType)
        chartType = params.chartType
        [colorCode: colorCode,team: team]
        //		println "projectId = "+projectId
    }
}
